/*
 *  Copyright 2008 The MITRE Corporation (http://www.mitre.org/). All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.mitre.lattice.graph;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.jgraph.graph.AttributeMap;
import org.jgraph.graph.ConnectionSet;
import org.jgraph.graph.DefaultEdge;
import org.jgraph.graph.DefaultGraphCell;
import org.jgraph.graph.DefaultGraphModel;
import org.jgraph.graph.DefaultPort;
import org.jgraph.graph.Edge;
import org.jgraph.graph.GraphCell;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphModel;
import org.mitre.lattice.lattice.LatticeNode;
import org.mitre.lattice.lattice.LatticeTree;
import org.mitre.mrald.util.Config;
/**
 *  Wrapper Class that encapsulates many functions within JGrpah. TO provide
 *  functions such as AddVertex, and AddEdge without having to repetively set up
 *  Ports and connection objects.
 *
 *@author     jchoyt
 *@created    November 12, 2003
 */
public class GraphFunctions
{
    static Map<DefaultGraphCell,AttributeMap> attributes = new HashMap<DefaultGraphCell,AttributeMap>();

    static ConnectionSet cs = new ConnectionSet();
    static Set<Edge> edges = new HashSet<Edge>();
    static GraphModel model = null;
    static Map vertexEdges = new HashMap();
    static Map<String,DefaultGraphCell> vertices = new HashMap<String,DefaultGraphCell>();


    /**
     *  Sets the upGraph attribute of the GraphFunctions class
     *
     *@return    Description of the Return Value
     */
    public static GraphModel setUpGraph()
    {
        model = new DefaultGraphModel();

        return model;
    }


    /**
     *  Takes the x value of the Node, and 'Snaps to Grid' to neaten
     *
     *@return    The allVertices value
     */
    public static Object[] getAllVertices()
    {
        Object[] cells = new Object[vertices.size()];
        int i = 0;
        Iterator iter = vertices.values().iterator();
        while ( iter.hasNext() )
        {
            i++;
            cells[i] = iter.next();
        }
        return cells;
    }


    /**
     *  Gets the root attribute of the GraphFunctions class
     *
     *@return    The root value
     */
    public static Object getRoot()
    {
        return vertices.get( LatticeTree.ROOT_LABEL );
    }


    /**
     *  Gets the vertex attribute of the GraphFunctions class
     *
     *@param  label  Description of the Parameter
     *@return        The vertex value
     */
    public static Object getVertex( String label )
    {
        return vertices.get( label );
    }


    /**
     *  Adds a feature to the Edge attribute of the GraphFunctions class
     *
     *@param  source  The feature to be added to the Edge attribute
     *@param  target  The feature to be added to the Edge attribute
     */
    public static void addEdge( String source, String target )
    {
        //addToModel( connectCells( source, target ) );
        addToModel( connectCells( target, source ) );
    }


    /**
     *  Adds a feature to the Vertex attribute of the GraphFunctions class
     *
     *@param  name  The feature to be added to the Vertex attribute
     *@return       Description of the Return Value
     */
    public static GraphCell addVertex( LatticeNode newNode)
    {
        //DefaultGraphCell vertex = new DefaultGraphCell( name );

        DefaultGraphCell vertex = Config.getLatticeFactory().createCell( newNode );


        vertex.add( new DefaultPort() );
        AttributeMap vertexAttrib = model.createAttributes();

        //GraphConstants.setBorderColor( vertexAttrib, Color.black );
        //GraphConstants.setBorder( vertexAttrib, BorderFactory.createRaisedBevelBorder() );

        attributes.put( vertex, vertexAttrib );
        vertices.put( newNode.getName(), vertex );
        return vertex;
    }


    /**
     *  Description of the Method
     *
     *@param  source  Description of the Parameter
     *@param  target  Description of the Parameter
     *@return         Description of the Return Value
     */
    public static boolean containsEdge( String source, String target )
    {
        if ( containsVertex( source ) && containsVertex( target ) )
        {
            if ( !vertexEdges.containsKey( source ) )
            {
                return false;
            }

            ArrayList edges = ( ArrayList ) vertexEdges.get( source );
            Iterator iter = edges.iterator();
            while ( iter.hasNext() )
            {
                Edge e = ( Edge ) iter.next();
                if ( e.getTarget().equals( getPort( target ) ) )
                {
                    return true;
                }
            }

        }
        return false;
    }


    /**
     *  Description of the Method
     *
     *@param  label  Description of the Parameter
     *@return        Description of the Return Value
     */
    public static boolean containsVertex( String label )
    {
        return vertices.containsKey( label );
    }


    /**
     *  Sets the visual attribute of the GraphFunctions class
     */
//    private static void setVisual()
//    {
        //Go through all the vertexs in Attributes and set to
        //same value

        //GraphConstants.setBounds(vertexAttrib, cellBounds);
        // Set black border

        //GraphConstants.setBorderColor(vertexAttrib, Color.black);
//    }


    /**
     *  Gets the port attribute of the GraphFunctions class
     *
     *@param  vertex  Description of the Parameter
     *@return         The port value
     */
    /* PM: Never used!
    private static DefaultPort getPort( DefaultGraphCell vertex )
    {
        return ( DefaultPort ) vertex.getChildAt( 0 );
    }*/


    /**
     *  Gets the port attribute of the GraphFunctions class
     *
     *@param  label  Description of the Parameter
     *@return        The port value
     */
    private static Object getPort( String label )
    {
        DefaultGraphCell cell = vertices.get( label );
        return ( DefaultPort ) cell.getChildAt( 0 );
    }


    /**
     *  Adds a feature to the Edge attribute of the GraphFunctions class
     *
     *@return    Description of the Return Value
     */
    private static DefaultEdge addEdge()
    {
        // Create Edge
        //
        DefaultEdge edge = new DefaultEdge();
        // Create Edge Attributes
        //
        AttributeMap edgeAttrib = model.createAttributes();
        // Set Arrow
        int arrow = GraphConstants.ARROW_DIAMOND;
        GraphConstants.setLineEnd( edgeAttrib, arrow );
        GraphConstants.setEndFill( edgeAttrib, true );
        GraphConstants.setLineColor( edgeAttrib, Color.BLUE );
        edges.add( edge );
        attributes.put( edge, edgeAttrib );
        return edge;
    }


    /**
     *  Adds a feature to the ToEdgeList attribute of the GraphFunctions class
     *
     *@param  source   The feature to be added to the ToEdgeList attribute
     *@param  newEdge  The feature to be added to the ToEdgeList attribute
     */
    @SuppressWarnings("unchecked")
    private static void addToEdgeList( String source, Edge newEdge )
    {
        if ( vertexEdges.containsKey( source ) )
        {
        	// PM: Assume the cast works properly.
            ArrayList<Edge> edges = ( ArrayList<Edge> ) vertexEdges.get( source );
            edges.add( newEdge );
        }
        else
        {
            ArrayList edges = new ArrayList();
            edges.add( newEdge );
        }

    }


    /**
     *  Adds a feature to the ToModel attribute of the GraphFunctions class
     *
     *@param  cells  The feature to be added to the ToModel attribute
     */
    public static void addToModel( Object[] cells )
    {
       model.insert( cells, attributes, cs, null, null );

    }


    /**
     *  Description of the Method
     *
     *@param  sourceName  Description of the Parameter
     *@param  targetName  Description of the Parameter
     *@return             Description of the Return Value
     */
    private static Object[] connectCells( String sourceName, String targetName )
    {
        // Connect Edge
        //
        DefaultEdge edge = addEdge();

        addToEdgeList( sourceName, edge );
        cs.connect( edge, getPort( sourceName ), getPort( targetName ) );
        return new Object[]{edge, vertices.get( sourceName ), vertices.get( targetName )};
    }


    /**
     *  Description of the Method
     *
     *@param  edge  Description of the Parameter
     *@return       Description of the Return Value
     */
    /* PM: Never used!
    private static boolean containsEdge( DefaultEdge edge )
    {
        return edges.contains( edge );
    }*/
}

